package shims

import "substr"
import "bytes"

import "fmt"

var _ = fmt.Println

//A wrapper to convert any func([]byte)bool into a Matcher
type FreeMatcher func([]byte) bool

func (f FreeMatcher) Match(s []byte) bool { return f(s) }

//A wrapper to convert any func([]byte,int)*S into a Finder
type FreeFinder func([]byte, int) *substr.S

func (f FreeFinder) Find(s []byte, n int) *substr.S { return f(s, n) }

//A wrapper to convert any func([]byte)[]byte into a Replacer
type FreeReplacer func([]byte) []byte

func (f FreeReplacer) Replace(s []byte) []byte { return f(s) }

type inverseFinder struct {
	i substr.Finder
}
//returns a finder that inverts the substrings
func Invert(f substr.Finder) substr.Finder { return &inverseFinder{f} }

func (i *inverseFinder) Find(s []byte, n int) *substr.S {
	m := n //inversion can be 2 less or 1 more so we add 2 to even it out
	if n > 0 {
		m += 2
	}
	return i.i.Find(s, m).Invert().Trunc(n)
}

type matchFinder struct {
	m substr.Matcher
}
//Turn a matcher into a finder that chooses the full string if true or none
//if false.
func MatcherToFinder(m substr.Matcher) substr.Finder { return &matchFinder{m} }
func (m *matchFinder) Find(s []byte, n int) *substr.S {
	out := substr.New(s, 1)
	if n != 0 && m.m.Match(s) {
		out.Push(0, -1)
	}
	return out
}

type findMatcher struct {
	f substr.Finder
}
//Take a finder and returns a matcher that returns true if Find(s, 1) has
//at least 1 match and false if there are no substrings found
func FinderToMatcher(f substr.Finder) substr.Matcher { return &findMatcher{f} }
func (f *findMatcher) Match(s []byte) bool {
	return len(f.f.Find(s, 1).Subs) != 0
}

// Break a string into substrings of length no greater than its underlying int,
//when used as a Finder.
// Matches strings of length no greater than its underlying int, when used as a
//Matcher.
// Truncates strings to length no greater than its underlying int, when used as
//a Replacer.
type Length int

// If b = 0 then return Runes.Find(s, n)
//
// If b < 0 then return all of s
//
// If b > 0 then split s into substrings of length b or less
func (b Length) Find(s []byte, n int) *substr.S {
	sln, ln := len(s), int(b)
	if ln == 0 {
		return Runes.Find(s, n)
	}
	if ln < 0 {
		out := substr.New(s, 1)
		out.Push(0, -1)
		return out
	}
	if n < 0 {
		n = sln/ln + 1
	}
	out := substr.New(s, n+1)
	for i := 0; i < sln && len(out.Subs) < n; i += ln {
		out.Push(i, i+ln)
	}
	return out.PushRest(true)
}

// If b <= 0 then match no strings
//
// Otherwise match strings whose length is less than or equal to b
//
// Use an_S.Drop(Length(0)) to remove empty matches
func (b Length) Match(s []byte) bool {
	return len(s) <= int(b)
}

// If b <= 0 then return nil
//
// Otherwise truncate s to length b if longer than b
func (b Length) Replace(s []byte) []byte {
	ln := int(b)
	if ln <= 0 {
		return nil
	}
	if len(s) <= ln {
		return s
	}
	ret := make([]byte, ln)
	copy(ret, s)
	return ret
}

type Bracket struct {
	begin, end []byte
}

type RBracket struct {
	begin, end []byte
}

func NewBracket(begin, end []byte) *Bracket {
	return &Bracket{begin, end}
}

func NewRBracket(begin, end []byte) *RBracket {
	return &RBracket{begin, end}
}

func (b *Bracket) Match(s []byte) bool {
	start := bytes.Index(s, b.begin)
	if start < 0 {
		return false
	}
	start += len(b.begin)
	if start < len(s) && bytes.Index(s[start:], b.end) >= 0 {
		return true
	}
	return false
}

func concat3(a, b, c []byte) []byte {
	la, lb, lc := len(a), len(b), len(c)
	out := make([]byte, la+lb+lc)
	copy(out[:la], a)
	copy(out[la:lb], b)
	copy(out[la+lb:], c)
	return out
}

func (b *Bracket) Replace(s []byte) []byte {
	return concat3(b.begin, s, b.end)
}

func (B *Bracket) Find(s []byte, n int) (out *substr.S) {
	out = substr.New(s, n)
	all := n < 0
	if all {
		n = len(s) //infinity
	} else if n == 0 {
		return
	}

	for a, b, m := 0, 0, 0; m < n && b < len(s); m++ {
		off := b
		a = bytes.Index(s[b:], B.begin)
		if a < 0 {
			break
		}
		a += off + len(B.begin)
		if a == len(s) {
			break
		}

		off = a
		b = bytes.Index(s[a:], B.end)
		if b < 0 {
			break
		}

		b += off
		out.Push(a, b) //step back past beginning of end-match
		b += len(B.end)
	}

	out.PushRest(!all)
	return
}

func (r *RBracket) rbrak1(s []byte) (int, int) {
	bf, be := r.begin[0], r.end[0]
	fl, el := len(r.begin), len(r.end)
	depth, save, c := 0, 0, 0
	for c < len(s) {
		if s[c] == bf && (fl == 1 || (c+fl < len(s) && bytes.Equal(s[c:c+fl], r.begin))) {
			c += fl
			depth++
			if depth == 1 {
				save = c
			}
		} else if s[c] == be && (el == 1 || (c+el < len(s) && bytes.Equal(s[c:c+el], r.end))) {
			depth--
			if depth == 0 {
				return save, c
			}
			c += el
		} else {
			c++
		}
	}
	return -1, -1
}

func (b *RBracket) Match(s []byte) bool {
	one, _ := b.rbrak1(s)
	return one != -1
}

func (b *RBracket) Replace(s []byte) []byte {
	return concat3(b.begin, s, b.end)
}

//This Finder on brackets "{" and "}", the input
//	a {b {c d}} e {f}
//returns the substrings
//	"b {c d}" and "f".
// If there are unbalanced brackets, neither match nor search for more matches,
// that is, given brackets "{" and "}" on
//	a {b {c d}} e {f}} {g}
// the substrings returned are
//	"b {c d}" and "f".
func (r *RBracket) Find(s []byte, n int) (out *substr.S) {
	out = substr.New(s, n)

	all := n < 0
	if all {
		n = len(s)
	} else if n == 0 {
		return
	}

	a, b, off := 0, 0, 0
	for m := 0; m < n; m++ {
		a, b = r.rbrak1(s[a:])
		if a == -1 {
			break
		}
		a += off
		b += off
		out.Push(a, b)
		a = b + len(r.end)
		off = a
	}

	out.PushRest(!all)
	return
}
